﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace CompilerForC.LexicalAnalysis
{
    /// <summary>
    /// Enumerates possible classifications of the recognized token.
    /// </summary>
    public enum TokenType
    {
        Unknown,
        Keyword,
        Operator,
        Identifier,
        Constant
    }

    /// <summary>
    /// Enumerates types of the constant value.
    /// </summary>
    public enum ConstantType
    {
        IntegerOctal,
        IntegerDecimal,
        IntegerHexadecimal,
        Float,
        Double,
        Character,
        String,
    }

    /// <summary>
    /// Encapsulates single token, a smallest unit of the lexical analysis.
    /// </summary>
    public class Token : IEquatable<Token>
    {
        #region Private Fields

        private TokenType _type = TokenType.Unknown;
        private int _lineNumber = -1;
        private int _tableIndex = -1;

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Gets or sets type of the token.
        /// </summary>
        public TokenType Type
        {
            get { return _type; }
            set { _type = value; }
        }

        /// <summary>
        /// Gets or sets line number in the original source code.
        /// </summary>
        public int LineNumber
        {
            get { return _lineNumber; }
            set { _lineNumber = value; }
        }

        /// <summary>
        /// Gets or sets index into the table depending on token type.
        /// </summary>
        public int TableIndex
        {
            get { return _tableIndex; }
            set { _tableIndex = value; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Constructs uninitialized token.
        /// </summary>
        public Token()
        {
        }

        /// <summary>
        /// Constructs and initializes token.
        /// </summary>
        /// <param name="type">Type of the token.</param>
        /// <param name="lineNumber">Line number in the original source code.</param>
        /// <param name="tableIndex">Index into the table depending on token type.</param>
        public Token(TokenType type, int lineNumber, int tableIndex)
        {
            _type = type;
            _lineNumber = lineNumber;
            _tableIndex = tableIndex;
        }

        #endregion Constructors

        #region Object Members

        /// <summary>
        /// Compares tokens for equality.
        /// </summary>
        /// <param name="obj">Object.</param>
        /// <returns>True if tokens are equal, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Token);
        }

        /// <summary>
        /// Obtains hash code for the token object.
        /// </summary>
        /// <returns>Hash code.</returns>
        public override int GetHashCode()
        {
            return _tableIndex ^ _lineNumber;
        }

        /// <summary>
        /// Returns string representation of the token object.
        /// </summary>
        public override string ToString()
        {
            return string.Format("Token: '{0}', '{1}', '{2}'", _type.ToString(), _lineNumber, _tableIndex);
        }

        #endregion Object Members

        #region IEquatable<Token> Members

        /// <summary>
        /// Compares tokens for equality.
        /// </summary>
        /// <param name="other">Other token.</param>
        /// <returns>True if tokens are equal, false otherwise.</returns>
        public bool Equals(Token other)
        {
            return other != null && _type.Equals(other._type) && _lineNumber.Equals(other._lineNumber) && _tableIndex.Equals(other._tableIndex);
        }

        #endregion
    }

    /// <summary>
    /// Encapsulates keyword table entry.
    /// </summary>
    public class Keyword : IEquatable<Keyword>
    {
        #region Private Fields

        private string _name = string.Empty;

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Gets or sets name of the keyword.
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Constructs uninitialized keyword.
        /// </summary>
        public Keyword()
        {
        }

        /// <summary>
        /// Constructs keyword of specified name.
        /// </summary>
        /// <param name="name">Name of the keyword.</param>
        public Keyword(string name)
        {
            _name = name;
        }

        #endregion Constructors

        #region Object Members

        /// <summary>
        /// Compares keywords for equality.
        /// </summary>
        /// <param name="obj">Object.</param>
        /// <returns>True if keywords are equal, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Keyword);
        }

        /// <summary>
        /// Obtains hash code for the keyword object.
        /// </summary>
        /// <returns>Hash code.</returns>
        public override int GetHashCode()
        {
            return _name.GetHashCode();
        }

        /// <summary>
        /// Returns string representation of the keyword object.
        /// </summary>
        public override string ToString()
        {
            return string.Format("Keyword: '{0}'", _name);
        }

        #endregion Object Members

        #region IEquatable<Keyword> Members

        /// <summary>
        /// Compares keywords for equality.
        /// </summary>
        /// <param name="other">Other keyword.</param>
        /// <returns>True if keywords are equal, false otherwise.</returns>
        public bool Equals(Keyword other)
        {
            return other != null && _name.Equals(other._name);
        }

        #endregion
    }

    /// <summary>
    /// Encapsulates operator table entry.
    /// </summary>
    public class Operator : IEquatable<Operator>
    {
        #region Private Fields

        private string _name = string.Empty;

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Gets or sets name of the operator.
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Constructs uninitialized operator.
        /// </summary>
        public Operator()
        {
        }

        /// <summary>
        /// Constructs operator of specified name.
        /// </summary>
        /// <param name="name">Name of the operator.</param>
        public Operator(string name)
        {
            _name = name;
        }

        #endregion Constructors

        #region Object Members

        /// <summary>
        /// Compares operators for equality.
        /// </summary>
        /// <param name="obj">Object.</param>
        /// <returns>True if operators are equal, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Operator);
        }

        /// <summary>
        /// Obtains hash code for the operator object.
        /// </summary>
        /// <returns>Hash code.</returns>
        public override int GetHashCode()
        {
            return _name.GetHashCode();
        }

        /// <summary>
        /// Returns string representation of the operator object.
        /// </summary>
        public override string ToString()
        {
            return string.Format("Operator: '{0}'", _name);
        }

        #endregion Object Members

        #region IEquatable<Operator> Members

        /// <summary>
        /// Compares operators for equality.
        /// </summary>
        /// <param name="other">Other operator.</param>
        /// <returns>True if operators are equal, false otherwise.</returns>
        public bool Equals(Operator other)
        {
            return other != null && _name.Equals(other._name);
        }

        #endregion
    }

    /// <summary>
    /// Encapsulates identifier table entry.
    /// </summary>
    public class Identifier : IEquatable<Identifier>
    {
        #region Private Fields

        private string _name = string.Empty;

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Gets or sets name of the identifier.
        /// </summary>
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Constructs uninitialized identifier.
        /// </summary>
        public Identifier()
        {
        }

        /// <summary>
        /// Constructs identifier of specified name.
        /// </summary>
        /// <param name="name">Name of the identifier.</param>
        public Identifier(string name)
        {
            Name = name;
        }

        #endregion Constructors

        #region Object Members

        /// <summary>
        /// Compares identifiers for equality.
        /// </summary>
        /// <param name="obj">Object.</param>
        /// <returns>True if identifiers are equal, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Identifier);
        }

        /// <summary>
        /// Obtains hash code for the identifier object.
        /// </summary>
        /// <returns>Hash code.</returns>
        public override int GetHashCode()
        {
            return _name.GetHashCode();
        }

        /// <summary>
        /// Returns string representation of the identifier object.
        /// </summary>
        public override string ToString()
        {
            return string.Format("Identifier: '{0}'", _name);
        }

        #endregion Object Members

        #region IEquatable<Identifier> Members

        /// <summary>
        /// Compares identifiers for equality.
        /// </summary>
        /// <param name="other">Other identifier.</param>
        /// <returns>True if identifiers are equal, false otherwise.</returns>
        public bool Equals(Identifier other)
        {
            return other != null && _name.Equals(other._name);
        }

        #endregion
    }

    /// <summary>
    /// Encapsulates constant table entry.
    /// </summary>
    public class Constant : IEquatable<Constant>
    {
        #region Private Fields

        private string _value = string.Empty;
        private ConstantType _type;

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Gets or sets string representation of the value of a constant.
        /// </summary>
        public string Value
        {
            get { return _value; }
            set { _value = value; }
        }

        /// <summary>
        /// Gets or sets type of the constant value.
        /// </summary>
        public ConstantType Type
        {
            get { return _type; }
            set { _type = value; }
        }

        #endregion Public Properties

        #region Constructors

        /// <summary>
        /// Constructs uninitialized constant.
        /// </summary>
        public Constant()
        {
        }

        /// <summary>
        /// Constructs constant of specified value.
        /// </summary>
        /// <param name="value">Value of the constant.</param>
        /// <param name="type">Type of the constant value.</param>
        public Constant(string value, ConstantType type)
        {
            Value = value;
            Type = type;
        }

        #endregion Constructors

        #region Object Members

        /// <summary>
        /// Compares constants for equality.
        /// </summary>
        /// <param name="obj">Object.</param>
        /// <returns>True if constants are equal, false otherwise.</returns>
        public override bool Equals(object obj)
        {
            return Equals(obj as Constant);
        }

        /// <summary>
        /// Obtains hash code for the constant object.
        /// </summary>
        /// <returns>Hash code.</returns>
        public override int GetHashCode()
        {
            return _value.GetHashCode();
        }

        /// <summary>
        /// Returns string representation of the constant object.
        /// </summary>
        public override string ToString()
        {
            return string.Format("Constant: '{0}'", _value);
        }

        #endregion Object Members

        #region IEquatable<Constant> Members

        /// <summary>
        /// Compares constants for equality.
        /// </summary>
        /// <param name="other">Other constant.</param>
        /// <returns>True if constants are equal, false otherwise.</returns>
        public bool Equals(Constant other)
        {
            return other != null && _value.Equals(other._value);
        }

        #endregion
    }

    /// <summary>
    /// Collection of tables filled by Lexer as a result of source code 
    /// tokenization.
    /// </summary>
    public class LexerTables
    {
        #region Private Fields

        private List<Token> _tokenList = new List<Token>();
        private List<LexerError> _errorList = new List<LexerError>();

        private Dictionary<int, Keyword> _keywordMap = new Dictionary<int, Keyword>();
        private Dictionary<int, Operator> _operatorMap = new Dictionary<int, Operator>();
        private Dictionary<int, Identifier> _identifierMap = new Dictionary<int, Identifier>();
        private Dictionary<int, Constant> _constantMap = new Dictionary<int, Constant>();

        #endregion Private Fields

        #region Public Properties

        /// <summary>
        /// Gets list of tokens.
        /// </summary>
        public List<Token> Tokens
        {
            get { return _tokenList; }
        }

        /// <summary>
        /// Gets list of errors.
        /// </summary>
        public List<LexerError> Errors
        {
            get { return _errorList; }
        }

        /// <summary>
        /// Gets map of keywords where key is index contained within token and 
        /// value is keyword name.
        /// </summary>
        public Dictionary<int, Keyword> Keywords
        {
            get { return _keywordMap; }
        }

        /// <summary>
        /// Gets map of operators where key is index contained within token and 
        /// value is operator.
        /// </summary>
        public Dictionary<int, Operator> Operators
        {
            get { return _operatorMap; }
        }

        /// <summary>
        /// Gets map of identifiers where key is index contained within token 
        /// and value is identifier name.
        /// </summary>
        public Dictionary<int, Identifier> Identifiers
        {
            get { return _identifierMap; }
        }

        /// <summary>
        /// Gets map of keywords where key is index contained within token and 
        /// value is string representation of the constants value.
        /// </summary>
        public Dictionary<int, Constant> Constants
        {
            get { return _constantMap; }
        }

        #endregion Public Properties

        #region Table Access Helper Methods

        /// <summary>
        /// Returns string representation of the specified token.
        /// </summary>
        /// <param name="token">Token.</param>
        /// <returns>String representation of the specified token or null if
        /// specified token does not have valid table index.</returns>
        public string GetTokenValue(Token token)
        {
            try
            {
                switch (token.Type)
                {
                case TokenType.Keyword:
                    return (_keywordMap[token.TableIndex] as Keyword).Name;

                case TokenType.Operator:
                    return (_operatorMap[token.TableIndex] as Operator).Name;

                case TokenType.Identifier:
                    return (_identifierMap[token.TableIndex] as Identifier).Name;

                case TokenType.Constant:
                    return (_constantMap[token.TableIndex] as Constant).Value;
                }
            }
            catch (Exception ex)
            {
                Trace.WriteLine(string.Format("Failed to query value for the token: {0}. Exception: {1}", token.ToString(), ex.Message));
            }

            return null;
        }

        /// <summary>
        /// Tries to retrieve keyword entry for the specified token.
        /// </summary>
        /// <param name="token">Token used to index keyword table.</param>
        /// <param name="keyword">Returns keyword if token is valid.</param>
        /// <returns>True if token is valid, false otherwise.</returns>
        public bool TryGetTableEntry(Token token, out Keyword keyword)
        {
            return _keywordMap.TryGetValue(token.TableIndex, out keyword);
        }

        /// <summary>
        /// Tries to retrieve operator entry for the specified token.
        /// </summary>
        /// <param name="token">Token used to index operator table.</param>
        /// <param name="keyword">Returns operator if token is valid.</param>
        /// <returns>True if token is valid, false otherwise.</returns>
        public bool TryGetTableEntry(Token token, out Operator op)
        {
            return _operatorMap.TryGetValue(token.TableIndex, out op);
        }

        /// <summary>
        /// Tries to retrieve identifier entry for the specified token.
        /// </summary>
        /// <param name="token">Token used to index identifier table.</param>
        /// <param name="keyword">Returns identifier if token is valid.</param>
        /// <returns>True if token is valid, false otherwise.</returns>
        public bool TryGetTableEntry(Token token, out Identifier identifier)
        {
            return _identifierMap.TryGetValue(token.TableIndex, out identifier);
        }

        /// <summary>
        /// Tries to retrieve constant entry for the specified token.
        /// </summary>
        /// <param name="token">Token used to index constant table.</param>
        /// <param name="keyword">Returns constant if token is valid.</param>
        /// <returns>True if token is valid, false otherwise.</returns>
        public bool TryGetTableEntry(Token token, out Constant constant)
        {
            return _constantMap.TryGetValue(token.TableIndex, out constant);
        }

        #endregion Table Access Helper Methods
    }
}
